
package horde.control;

// <editor-fold defaultstate="collapsed" desc="Imports">
import horde.interfaces.ItfBehavior;
import horde.patterns.Core;
import horde.patterns.Msg;
import horde.utils.ParseXML;
import horde.utils.ParseXML.Node;
import java.util.ArrayList;
import java.util.HashMap;
// </editor-fold>

/**
 * @author Arturo Mazón and Andres Picazo
 */
public class AutomatonBehavior implements ItfBehavior {
    
    // <editor-fold defaultstate="collapsed" desc="State class definition">
    public class Action {
        String request, run, nextStateIfOk, onErrorDo;
        public Action (String request, String run, String next, String onError) {
            this.request = request;
            this.run = run;
            this.nextStateIfOk = next;
            this.onErrorDo = onError;
        }
        public String getRequest () {
            return request;
        }
    }
    public class State {
        
        String name;
        HashMap actions;

        public State (String name) {
            this.name = name;
            actions = new HashMap();
        }

        public boolean addAction (Action a) {
            /* A given state cannot have two requests with the same name */
            if (actions.containsKey(a.request)) return false;
            else {
                actions.put(a.request, a);
                return true;
            }
        }

        public boolean hasAction (String run) {
            return actions.containsKey(run);
        }

        public String getNextState (String action) {
            if (actions.containsKey(action)) {
                Action a = (Action)actions.get(action);
                return a.nextStateIfOk;
            } else return null;
        }

        public String getStateForError (String action) {
            if (actions.containsKey(action)) {
                Action a = (Action)actions.get(action);
                return a.onErrorDo;
            } else return null;
        }

        public String getMethodToRun (String action) {
            if (actions.containsKey(action)) {
                Action a = (Action)actions.get(action);
                return a.run;
            } else return null;
        }

        public Object [] getActions () {
            return actions.values().toArray();
        }

        /* Return the method to run without a request, if it exists */
        public Action hasAutoAction () {
            if (actions.containsKey("")) return (Action)actions.get("");
            else return null;
        }
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Constructor">
    public AutomatonBehavior (String pathToDescription) {
        states = new HashMap();
        initFromXML (pathToDescription);
        currentState = (State)states.get(nextState);
        debug = false;
    }

    public AutomatonBehavior (String pathToDescription, boolean debug) {
        states = new HashMap();
        initFromXML (pathToDescription);
        currentState = (State)states.get(nextState);
        this.debug = debug;
        if (debug) System.err.println ("Current state is: " + currentState.name);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Private methods">
    private void initFromXML (String pathToDescription) {
        Node currentNode = ParseXML.parseFile(pathToDescription);
        ArrayList<Node> childs = currentNode.getChildNodes();
        for (Node n : childs) {
            if (n.getName().toLowerCase().equals("initialstate")) {
                nextState = n.getProperty("name");
                if (debug) System.err.println ("Next state = " + nextState);
            }
            /* Parse each state */
            else if (n.getName().toLowerCase().equals("state")) {
                State s = new State(n.getProperty("name"));
                if (debug) System.err.println ("State: " + s.name);
                /* Parse actions */
                ArrayList<Node> parsedActions = n.getChildNodes();
                for (Node k : parsedActions) {
                    String request = k.getProperty("request");
                    String run = k.getProperty("run");
                    String ifOk = k.getProperty("ifOk");
                    String onError = k.getProperty("onError");
                    if (debug) System.err.printf ("\tNew action (%s,%s,%s,%s)\n", request, run, ifOk, onError);
                    s.addAction(new Action(request, run, ifOk, onError));
                } states.put(s.name, s);
            }
        }
    }

    // </editor-fold>

    public boolean process(Msg msg) {
        if (msg.getType().equals(Msg.TYPE_REQUEST)) {
            Object [] trunk = msg.getTrunk();
            if (trunk != null) {
                String request = (String)trunk[0];
                // Check if the current state can process the task
                if (currentState.hasAction(request)) {
                    /* Change the trunk[0] from request to run field */
                    trunk[0] = currentState.getMethodToRun(request);
                    nextState = currentState.getNextState(request);
                    /* Core run might change nextState */
                    boolean exitValue = core.runTask(trunk);
                    /* Try to change status, if needed */
                    if (!nextState.equals(currentState.name)) {
                        /* Change status */
                        currentState = (State)states.get(nextState);
                        Action auto = currentState.hasAutoAction();
                        if (auto != null) core.runTask(auto.run);
                    }
                    return exitValue;
                }
                else {
//                    requests.trace("", "Action " + request + " can't be attended on state " + currentState.name, "");
                    return false;
                }
            }
            else {
//                requests.trace("", "Can't process request: trunk is empty", "");
                return false;
            }
        } else return false;
    }

    // <editor-fold defaultstate="collapsed" desc="Public methods">
    public void setCore(Core core) {
        this.core = core;
    }

    public Core getCore() {
        return core;
    }

    public Object [] getStates () {
        return states.values().toArray();
    }

    public State getCurrentState () {
        return currentState;
    }

    public String getCurrentStateName () {
        return currentState.name;
    }

    public String getDefaultActionRequest () {
        if (currentState.hasAutoAction() != null) {
            Action a = currentState.hasAutoAction();
            return a.request;
        } else return null;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Attributes">
    private HashMap states;
    private String nextState;
    private Core core;
    private State currentState;
    private boolean debug;
    // </editor-fold>
}
